﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Browser;//needed for scripting
using System.Xml; //for list activity data
using System.IO;

//used by ColorSupport class
using System.Collections.Generic;
using System.Reflection;

namespace agSPListActivityDisplayLib
{
    [Scriptable]
    public partial class DisplayListActivity : Canvas
    {
        private HtmlDocument _doc;
        private string _activityXml;
        private double _maxActivity = 0;
        private bool _Display0ActivityLists = true;
        private bool _OpenListInNewWindow = true;
        private string _bar1Color = "Colors.Orange";
        private string _bar2Color = "Colors.Green";
        private string _silverlightJsUrl = "";
        private string _silverlightControlId = "";
        private string _xamlUrl = "";
        private string _siteUrl = "";
        private bool _showSiteTitle = false;

        public void Page_Loaded(object o, EventArgs e)
        {
            // Required to initialize variables
            InitializeComponent();

            //create an entry point for javascript-callable methods/properties
            WebApplication.Current.RegisterScriptableObject("listActivity", this);

            _doc = HtmlPage.Document;

        }

        /// <summary>
        /// Automates creation of a Gradient Brush used in painting the bars (in ShowBars)
        /// </summary>
        /// <param name="startColor"></param>
        /// <param name="endColor"></param>
        /// <returns></returns>
        internal LinearGradientBrush CreateLinearBrush(Color startColor, Color endColor)
        {
            LinearGradientBrush linearBrush;
            GradientStop gs;
            gs = new GradientStop();
            gs.Color = startColor;
            gs.Offset = 0;

            linearBrush = new LinearGradientBrush();
            linearBrush.GradientStops.Add(gs);
            gs = new GradientStop();
            gs.Color = endColor;
            gs.Offset = 1;
            linearBrush.GradientStops.Add(gs);
            linearBrush.StartPoint = new Point(0, 0.5);
            linearBrush.EndPoint = new Point(1, 0.5);

            return linearBrush;

        }

        //TODO: re-work ShowBars to remove 2nd iteration through XML tree
        /// <summary>
        /// Displays bars under each Yesterday/Today label, indicating relative activity volume.
        /// </summary>
        internal void ShowBars()
        {
            System.Xml.XmlReader xr;
            System.IO.StringReader sr;
            string listTitle, yesterdayActivity, todayActivity, listUrl;
            double totalActivity = 0;
            Rectangle rectBar;

            sr = new System.IO.StringReader(_activityXml);
            xr = XmlReader.Create(sr);

            while (xr.Read())
            {
                if ((xr.LocalName == "Lists") && (xr.NodeType == XmlNodeType.Element))
                {
                    using (XmlReader listNode = xr.ReadSubtree())
                    {
                        //iterate through List nodes
                        do
                        {
                            //get data from XML
                            listNode.ReadToFollowing("ListTitle");
                            listTitle = listNode.ReadInnerXml();
                            yesterdayActivity = listNode.ReadInnerXml();
                            todayActivity = listNode.ReadInnerXml();
                            listUrl = listNode.ReadInnerXml();

                            totalActivity = double.Parse(yesterdayActivity) + double.Parse(todayActivity);

                            //if (no activity AND display0listactivity is true) or if there is activity, create a textblock
                            if (((0 == totalActivity) && (_Display0ActivityLists)) || (0 < totalActivity))
                            {

                                TextBlock tb = cvsText.FindName(listTitle.Replace(" ", "_")) as TextBlock;
                                double canvasWidth = (double)cvsText.GetValue(Canvas.WidthProperty);

                                rectBar = new Rectangle();
                                rectBar.SetValue(Canvas.TopProperty, (double)tb.GetValue(Canvas.TopProperty) + tb.FontSize + 5);
                                rectBar.SetValue(Rectangle.HeightProperty, tb.FontSize);
                                rectBar.SetValue(Rectangle.WidthProperty, (double.Parse(yesterdayActivity) / _maxActivity) * canvasWidth);
                                rectBar.Fill = CreateLinearBrush(Colors.White, GetColorFromString(_bar1Color));
                                rectBar.SetValue(Canvas.ZIndexProperty, 0);

                                //add textblock to the canvas
                                cvsText.Children.Add(rectBar);

                                rectBar = new Rectangle();
                                rectBar.SetValue(Canvas.TopProperty,
                                    (double)tb.GetValue(Canvas.TopProperty) + 2 * tb.FontSize + 7);
                                rectBar.SetValue(Rectangle.HeightProperty, tb.FontSize);
                                rectBar.SetValue(Rectangle.WidthProperty, (double.Parse(todayActivity) / _maxActivity) * canvasWidth);
                                rectBar.Fill = CreateLinearBrush(Colors.White, GetColorFromString(_bar2Color));


                                rectBar.SetValue(Canvas.ZIndexProperty, 0);

                                //add textblock to the canvas
                                cvsText.Children.Add(rectBar);
                            }
                        } while (listNode.ReadToFollowing("List"));
                    }
                }
            }
            sr.Close();

        }

        /// <summary>
        /// This function is called from javascript and receives options information for the code. It must
        /// be called before UpdateDisplay for code to transfer web part properties to Silverlight.
        /// </summary>
        /// <param name="optionsXml">XML string containing the data.</param>
        [Scriptable]
        public void SetOptions(string optionsXml)
        {
            System.Xml.XmlReader xr;
            System.IO.StringReader sr;

            sr = new System.IO.StringReader(optionsXml);
            xr = XmlReader.Create(sr);

            while (xr.Read())
            {
                if ((xr.LocalName == "PropertyBag") && (xr.NodeType == XmlNodeType.Element))
                {
                    using (XmlReader optionsNode = xr.ReadSubtree())
                    {
                        while (optionsNode.ReadToFollowing("Property"))
                        {
                            optionsNode.MoveToAttribute("Name");

                            switch (optionsNode.Value)
                            {
                                case "DisplayZeroActivityLists":
                                    optionsNode.MoveToAttribute("Value");
                                    _Display0ActivityLists = Convert.ToBoolean(optionsNode.Value);
                                    break;
                                case "OpenListInNewWindow":
                                    optionsNode.MoveToAttribute("Value");
                                    _OpenListInNewWindow = Convert.ToBoolean(optionsNode.Value);
                                    break;
                                case "Bar1Color":
                                    optionsNode.MoveToAttribute("Value");
                                    _bar1Color = "Colors." + optionsNode.Value;
                                    break;
                                case "Bar2Color":
                                    optionsNode.MoveToAttribute("Value");
                                    _bar2Color = "Colors." + optionsNode.Value;
                                    break;
                                case "SilverlightControlId":
                                    optionsNode.MoveToAttribute("Value");
                                    _silverlightControlId = optionsNode.Value;
                                    break;
                                case "SilverlightJsUrl":
                                    optionsNode.MoveToAttribute("Value");
                                    _silverlightJsUrl = optionsNode.Value;
                                    break;
                                case "XamlUrl":
                                    optionsNode.MoveToAttribute("Value");
                                    _xamlUrl = optionsNode.Value;
                                    break;
                                case "SharePointSiteUrl":
                                    optionsNode.MoveToAttribute("Value");
                                    _siteUrl = optionsNode.Value;
                                    break;
                                case "ShowSiteTitle":
                                    optionsNode.MoveToAttribute("Value");
                                    _showSiteTitle = Convert.ToBoolean(optionsNode.Value);
                                    break;
                            }
                        }
                    }
                }
            }
            sr.Close();

        }

        /// <summary>
        /// This function is called from the web part and receives list information in the parameter.
        /// It iterates over list data, recording maximum activity (to be used by ShowBars for scaling
        /// bars), and dynamically creating TextBlock elements to display list name and Yesterday and Today's
        /// activity. SetOptions must be called before this one in order to propagate web part property values.
        /// </summary>
        /// <param name="activityXml">XML string containing the data.</param>
        /// <returns>If successful, returns a blank string to the calling javascript function. An 
        /// unsuccessful scenario isn't currently implemented</returns>
        [Scriptable]
        public void UpdateDisplay(string activityXml)
        {
            _activityXml = activityXml;

            System.Xml.XmlReader xr;
            System.IO.StringReader sr;
            TextBlock txtListData;
            Run ilRun;
            string listTitle, yesterdayActivity, todayActivity, listUrl;
            double totalHeight = 0;
            double maxWidth = 0;
            double totalActivity = 0;

            sr = new System.IO.StringReader(activityXml);
            xr = XmlReader.Create(sr);

            cvsText.Children.Clear();

            while (xr.Read())
            {
                if ((xr.LocalName == "SiteInfo") && (xr.NodeType == XmlNodeType.Element) && (_showSiteTitle == true))
                {
                    using (XmlReader listNode = xr.ReadSubtree())
                    {
                        while (listNode.Read())
                        {
                            if (listNode.LocalName == "SiteTitle")
                            {
                                txtListData = new TextBlock();
                                txtListData.Text = xr.ReadInnerXml();
                                //txtListData.SetValue(TextBlock.NameProperty, txtListData.Text.Replace(" ", "_"));
                                txtListData.SetValue(TextBlock.NameProperty, "siteName");
                                txtListData.Foreground = new SolidColorBrush(GetColorFromString("White"));
                                txtListData.SetValue(Canvas.TopProperty, 0);
                                txtListData.FontSize = 12;
                                maxWidth = txtListData.ActualWidth + 20;
                                cvsTitle.Background = CreateLinearBrush(GetColorFromString("DarkGray"), GetColorFromString("White"));
                                cvsTitle.Children.Add(txtListData);
                                txtListData = null;

                            }
                        }
                    }
                }

                if ((xr.LocalName == "Lists") && (xr.NodeType == XmlNodeType.Element))
                {
                    using (XmlReader listNode = xr.ReadSubtree())
                    {
                        //iterate through List nodes
                        while (listNode.ReadToFollowing("List"))
                        {
                            //get data from XML
                            listNode.ReadToFollowing("ListTitle");
                            listTitle = listNode.ReadInnerXml();
                            yesterdayActivity = listNode.ReadInnerXml();
                            todayActivity = listNode.ReadInnerXml();
                            listUrl = listNode.ReadInnerXml();

                            totalActivity = double.Parse(yesterdayActivity) + double.Parse(todayActivity);

                            //if (no activity AND display0listactivity is true) or if there is activity, create a textblock
                            if (((0 == totalActivity) && (_Display0ActivityLists)) || (0 < totalActivity))
                            {
                                //create a new text block
                                txtListData = new TextBlock();
                                txtListData.Text = "";
                                txtListData.SetValue(TextBlock.NameProperty, listTitle.Replace(" ", "_"));
                                txtListData.MouseEnter += new MouseEventHandler(txtListData_MouseEnter);
                                txtListData.MouseLeave += new EventHandler(txtListData_MouseLeave);
                                txtListData.MouseLeftButtonDown += new MouseEventHandler(txtListData_MouseLeftButtonDown);
                                txtListData.Tag = listUrl;
                                txtListData.SetValue(Canvas.TopProperty, totalHeight);
                                txtListData.Cursor = Cursors.Hand;
                                txtListData.SetValue(Canvas.ZIndexProperty, 1);

                                //add data to the text block, with some formatting along the way
                                //TODO: move formatting details into Web Part properties for configurability
                                //TODO: localize hardcoded text...
                                ilRun = new Run();
                                ilRun.FontSize = 12;
                                ilRun.SetValue(Run.FontWeightProperty, FontWeights.Bold);
                                ilRun.SetValue(Run.FontStyleProperty, FontStyles.Italic);
                                ilRun.Text = listTitle;
                                txtListData.Inlines.Add(ilRun);
                                txtListData.Inlines.Add(new LineBreak());

                                ilRun = new Run();
                                ilRun.Text = "Yesterday: " + yesterdayActivity;
                                ilRun.FontSize = 11;
                                txtListData.Inlines.Add(ilRun);
                                txtListData.Inlines.Add(new LineBreak());

                                ilRun = new Run();
                                ilRun.Text = "Today: " + todayActivity;
                                ilRun.FontSize = 11;
                                txtListData.Inlines.Add(ilRun);
                                txtListData.Inlines.Add(new LineBreak());

                                //track height & width of resulting text block for 
                                //silverlight control resizing purposes
                                totalHeight += txtListData.ActualHeight;
                                maxWidth = Math.Max(txtListData.ActualWidth, maxWidth);

                                //track max change value for graphing purposes
                                _maxActivity = Math.Max(Math.Max(double.Parse(yesterdayActivity), double.Parse(todayActivity)), _maxActivity);

                                //add textblock to the canvas
                                cvsText.Children.Add(txtListData);
                                txtListData = null;

                            }
                        }
                    }
                }
            }

            sr.Close();

            //set canvas size properties
            maxWidth = Math.Max(double.Parse((cvsText.Parent as Canvas).GetValue(Canvas.WidthProperty).ToString()), maxWidth);
            (cvsText.Parent as Canvas).SetValue(Canvas.WidthProperty, maxWidth);
            (cvsText.Parent as Canvas).SetValue(Canvas.HeightProperty, totalHeight);
            cvsText.SetValue(Canvas.HeightProperty, totalHeight);
            cvsText.SetValue(Canvas.WidthProperty, maxWidth);

            cvsTitle.SetValue(Canvas.WidthProperty, maxWidth);

            _doc.GetElementByID(_silverlightControlId).SetAttribute("width", (cvsText.Parent as Canvas).GetValue(Canvas.WidthProperty).ToString());
            _doc.GetElementByID(_silverlightControlId).SetAttribute("height", totalHeight.ToString());

            //Display graphs under the text
            //this should be refactored to remove a 2nd iteration over the XML data
            ShowBars();

        }

        /// <summary>
        /// Handles the List Activity text block click event and, depending on web part options,
        /// opens the list in a new window or the same window.
        /// </summary>
        /// <param name="sender">TextBlock</param>
        /// <param name="e">MouseEventArgs</param>
        void txtListData_MouseLeftButtonDown(object sender, MouseEventArgs e)
        {
            TextBlock tb = sender as TextBlock;
            try
            {
                switch (_OpenListInNewWindow)
                {
                    case true:
                        HtmlPage.Navigate(tb.Tag, "_blank");
                        break;
                    case false:
                        HtmlPage.Navigate(tb.Tag, "_self");
                        break;
                }
            }
            finally
            {
                tb = null;
            }
        }

        /// <summary>
        /// Handles the mouse over event over the text block with list activity information. It changes
        /// the color of the text block's font to Black.
        /// </summary>
        /// <param name="sender">sender is a dynamically created TextBlock</param>
        /// <param name="e">e contains MouseEventArgs</param>
        void txtListData_MouseLeave(object sender, EventArgs e)
        {
            TextBlock tb = sender as TextBlock;
            tb.Foreground = new SolidColorBrush(Colors.Black);
        }
        /// <summary>
        /// Handles the mouse over event over the text block with list activity information. It changes
        /// the color of the text block's font to Blue.
        /// </summary>
        /// <param name="sender">sender is a dynamically created TextBlock</param>
        /// <param name="e">e contains MouseEventArgs</param>
        void txtListData_MouseEnter(object sender, MouseEventArgs e)
        {
            TextBlock tb = sender as TextBlock;
            tb.Foreground = new SolidColorBrush(Colors.Blue);
        }

        /// <summary>
        /// Returns Color object (Media namespace) based on color string. Currently supported colors:
        /// Red, Green, Blue, Orange, Purple, DarkBlue, Black, White, LightGray, DarkGray,
        /// Yellow, Fuscia, Maroon, Periwinkle, Pink
        /// </summary>
        /// <param name="colorName">String containing color name</param>
        /// <returns>Color object</returns>
        private Color GetColorFromString(string colorName)
        {
            Color color = new Color();

            if (!colorName.StartsWith("Colors."))
            {
                colorName = "Colors." + colorName;
            }
            switch (colorName)
            {
                case "Colors.Red":
                    color = Color.FromRgb(255, 0, 0);
                    break;
                case "Colors.Green":
                    color = Color.FromRgb(0, 255, 0);
                    break;
                case "Colors.Blue":
                    color = Color.FromRgb(0, 0, 255);
                    break;
                case "Colors.Orange":
                    color = Color.FromRgb(255, 128, 0);
                    break;
                case "Colors.Purple":
                    color = Color.FromRgb(128, 0, 255);
                    break;
                case "Colors.DarkBlue":
                    color = Color.FromRgb(0, 0, 192);
                    break;
                case "Colors.Black":
                    color = Color.FromRgb(0, 0, 0);
                    break;
                case "Colors.White":
                    color = Color.FromRgb(255, 255, 255);
                    break;
                case "Colors.LightGray":
                    color = Color.FromRgb(192, 192, 192);
                    break;
                case "Colors.DarkGray":
                    color = Color.FromRgb(128, 128, 128);
                    break;
                case "Colors.Yellow":
                    color = Color.FromRgb(255, 255, 0);
                    break;
                case "Colors.Fuscia":
                    color = Color.FromRgb(255, 0, 255);
                    break;
                case "Colors.Maroon":
                    color = Color.FromRgb(128, 0, 0);
                    break;
                case "Colors.Periwinkle":
                    color = Color.FromRgb(128, 128, 255);
                    break;
                case "Colors.Aqua":
                    color = Color.FromRgb(0, 255, 255);
                    break;
                case "Colors.Pink":
                    color = Color.FromRgb(255, 192, 255);
                    break;

            }
            return color;
        }

    }
}
